/************************************************************************
                                                                     
   Xceed Ultimate ListBox for Silverlight                                                                                                                                            
   Copyright (C) 2010 Xceed Software Inc.    
                                                                     
   This program is provided to you under the terms of the GNU General Public  
   License version 2 as published by the Free Software Foundation. 
        
   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY, without even the implied warranty of 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License along 
   with this program, if not, write to the Free Software Foundation, Inc., 
   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

   This program can be provided to you by Xceed Software Inc. under a
   proprietary commercial license agreement for use in non-Open Source
   projects. Visit Xceed.com to find the commercial edition of 
   Xceed Ultimate ListBox for Silverlight.                                    
                                                                      
 **********************************************************************/

using System;
using System.Diagnostics;
using Xceed.Silverlight.Compatibility;

namespace Xceed.Silverlight.Data
{
  [DebuggerDisplay( "FilterBy = {DebuggerDisplayString}" )]
  public sealed class FilterExpression : FilterExpressionBase
  {
    #region Constructors

    public FilterExpression( string memberName, FilterOperator filterOperator, object value )
      : this( memberName, filterOperator, value, null, null )
    {
    }

    public FilterExpression( string memberName, FilterOperator filterOperator, string value, bool? isCaseSensitive )
      : this( memberName, filterOperator, value, isCaseSensitive, null )
    {
    }

    public FilterExpression( string memberName, FilterOperator filterOperator, DateTime value, bool? isTimeSensitive )
      : this( memberName, filterOperator, value, null, isTimeSensitive )
    {
    }

    public FilterExpression()
    {
      //Note: This constructor is there to allow XAML declaration of FilterExpressions
    }

    internal FilterExpression( string memberName, FilterOperator filterOperator, object value, bool? isCaseSensitive, bool? isTimeSensitive )
    {
      this.Initialize( memberName, filterOperator, value, isCaseSensitive, isTimeSensitive );
    }

    private FilterExpression( FilterExpression copy )
    {
      this.Initialize( copy.MemberName, copy.FilterOperator, copy.Value, copy.IsCaseSensitive, copy.IsTimeSensitive );
    }

    #endregion

    #region MemberName Property

    public string MemberName
    {
      get
      {
        return m_memberName;
      }
      set
      {
        if( this.IsFrozen )
          throw new InvalidOperationException( "A FilterExpression cannot be modified once it has been frozen." );

        if( value != m_memberName )
        {
          m_memberName = value;
        }
      }
    }

    private string m_memberName;

    #endregion

    #region FilterOperator Property

    public FilterOperator FilterOperator
    {
      get
      {
        return m_operator;
      }
      set
      {
        if( this.IsFrozen )
          throw new InvalidOperationException( "A FilterExpression cannot be modified once it has been frozen." );

        if( value != m_operator )
        {
          m_operator = value;
        }
      }
    }

    private FilterOperator m_operator;

    #endregion

    #region Value Property

    public object Value
    {
      get
      {
        return m_value;
      }
      set
      {
        if( this.IsFrozen )
          throw new InvalidOperationException( "A FilterExpression cannot be modified once it has been frozen." );

        if( value != m_value )
        {
          m_value = value;
        }
      }
    }

    private object m_value; //null

    #endregion

    #region IsCaseSensitive Property

    public bool? IsCaseSensitive
    {
      get
      {
        return m_isCaseSensitive;
      }
      set
      {
        if( this.IsFrozen )
          throw new InvalidOperationException( "A FilterExpression cannot be modified once it has been frozen." );

        if( value != m_isCaseSensitive )
        {
          m_isCaseSensitive = value;
        }
      }
    }

    private bool? m_isCaseSensitive;

    #endregion

    #region IsTimeSensitive Property

    public bool? IsTimeSensitive
    {
      get
      {
        return m_isTimeSensitive;
      }
      set
      {
        if( this.IsFrozen )
          throw new InvalidOperationException( "A FilterExpression cannot be modified once it has been frozen." );

        if( value != m_isTimeSensitive )
        {
          m_isTimeSensitive = value;
        }
      }
    }

    private bool? m_isTimeSensitive;

    #endregion

    public override int GetHashCode()
    {
#if WINDOWS_PHONE
      this.Freeze();
#endif

      if( m_memberName == null )
        return 0;

      return m_memberName.GetHashCode();
    }

    public override bool Equals( object obj )
    {
#if WINDOWS_PHONE
      this.Freeze();
#endif

      if( object.ReferenceEquals( this, obj ) )
        return true;

      FilterExpression expression = obj as FilterExpression;
      if( expression == null )
        return false;

      if( expression.GetHashCode() != this.GetHashCode() )
        return false;

      return ( object.Equals( expression.FilterOperator, this.FilterOperator ) )
          && ( object.Equals( expression.IsCaseSensitive, this.IsCaseSensitive ) )
          && ( object.Equals( expression.IsTimeSensitive, this.IsTimeSensitive ) )
          && ( object.Equals( expression.MemberName, this.MemberName ) )
          && ( object.Equals( expression.Value, this.Value ) );
    }

    internal override FilterExpressionBase Clone()
    {
#if WINDOWS_PHONE
      this.Freeze();
#endif

      return new FilterExpression( this );
    }

    [DebuggerStepThrough]
    internal override void Accept( FilterExpressionVisitor visitor )
    {
#if WINDOWS_PHONE
      this.Freeze();
#endif

      if( visitor != null )
      {
        visitor.Visit( this );
      }
    }

    internal override void FreezeOverride()
    {
      // For Phone SDK Compatibility
      if( StringHelper.IsNullOrWhiteSpace( m_memberName ) )
        throw new InvalidOperationException( "A MemberName must be specified in order for the FilterExpression to be valid." );

      if( !Enum.IsDefined( typeof( FilterOperator ), m_operator ) )
        throw new InvalidOperationException( "A FilterOperator must be specified in order for the FilterExpression to be valid." );

      base.FreezeOverride();
    }

    private void Initialize(
      string memberName,
      FilterOperator filterOperator,
      object value,
      bool? isCaseSensitive,
      bool? isTimeSensitive )
    {
      m_memberName = memberName;
      m_operator = filterOperator;
      m_value = value;
      m_isCaseSensitive = isCaseSensitive;
      m_isTimeSensitive = isTimeSensitive;
    }
  }
}
